﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Heaven.Texturing.Importers.DirectDraw
{
    /// <summary>
    /// Формат пикселей
    /// </summary>
    class PixelFormat
    {
        #region Атрибуты

        // Flags to indicate valid fields. Uncompressed formats will usually use DDPF_RGB to indicate an RGB format, while compressed formats will use DDPF_FOURCC with a four-character code.
        private int flags;
        // This is the four-character code for compressed formats. dwFlags should include DDPF_FOURCC in this case. For DXTn compression, this is set to "DXT1", "DXT2", "DXT3", "DXT4", or "DXT5".
        private int fourCC;
        // For RGB formats, this is the total number of bits in the format. dwFlags should include DDPF_RGB in this case. This value is usually 16, 24, or 32. For A8R8G8B8, this value would be 32.        
        private int rgbBitCount;
        // For RGB formats, these three fields contain the masks for the red, green, and blue channels. For A8R8G8B8, these values would be 0x00ff0000, 0x0000ff00, and 0x000000ff respectively.
        private int redBitMask;
        // For RGB formats, these three fields contain the masks for the red, green, and blue channels. For A8R8G8B8, these values would be 0x00ff0000, 0x0000ff00, and 0x000000ff respectively.
        private int greenBitMask;
        // For RGB formats, these three fields contain the masks for the red, green, and blue channels. For A8R8G8B8, these values would be 0x00ff0000, 0x0000ff00, and 0x000000ff respectively.
        private int blueBitMask;
        // For RGB formats, this contains the mask for the alpha channel, if any. dwFlags should include DDPF_ALPHAPIXELS in this case. For A8R8G8B8, this value would be 0xff000000.
        private int alphaBitMask;

        #endregion

        #region Свойства

        /// <summary>
        /// Flags to indicate valid fields. Uncompressed formats will usually use DDPF_RGB to indicate an RGB format, while compressed formats will use DDPF_FOURCC with a four-character code.
        /// </summary>
        public int Flags
        {
            get { return flags; }
        }

        /// <summary>
        /// This is the four-character code for compressed formats. dwFlags should include DDPF_FOURCC in this case. For DXTn compression, this is set to "DXT1", "DXT2", "DXT3", "DXT4", or "DXT5".
        /// </summary>
        public int FourCC
        {
            get { return fourCC; }
        }

        /// <summary>
        /// For RGB formats, this is the total number of bits in the format. dwFlags should include DDPF_RGB in this case. This value is usually 16, 24, or 32. For A8R8G8B8, this value would be 32.        
        /// </summary>
        public int RgbBitCount
        {
            get { return rgbBitCount; }
        }

        /// <summary>
        /// For RGB formats, these three fields contain the masks for the red, green, and blue channels. For A8R8G8B8, these values would be 0x00ff0000, 0x0000ff00, and 0x000000ff respectively.
        /// </summary>
        public int RedBitMask
        {
            get { return redBitMask; }
        }

        /// <summary>
        /// For RGB formats, these three fields contain the masks for the red, green, and blue channels. For A8R8G8B8, these values would be 0x00ff0000, 0x0000ff00, and 0x000000ff respectively.
        /// </summary>
        public int GreenBitMask
        {
            get { return greenBitMask; }
        }

        /// <summary>
        /// For RGB formats, these three fields contain the masks for the red, green, and blue channels. For A8R8G8B8, these values would be 0x00ff0000, 0x0000ff00, and 0x000000ff respectively.
        /// </summary>
        public int BlueBitMask
        {
            get { return blueBitMask; }
        }

        /// <summary>
        /// // For RGB formats, this contains the mask for the alpha channel, if any. dwFlags should include DDPF_ALPHAPIXELS in this case. For A8R8G8B8, this value would be 0xff000000.
        /// </summary>
        public int AlphaBitMask
        {
            get { return alphaBitMask; }
        }

        #endregion

        #region Инициализация

        /// <summary>
        /// Конструктор
        /// </summary>
        /// <param name="reader">Ридер данных</param>
        public PixelFormat(BinaryReader reader)
        {
            int size = reader.ReadInt32();
            if (size != 32) throw new ApplicationException("Некорректный размер формата пикселя.");
            flags = reader.ReadInt32();
            fourCC = reader.ReadInt32();
            rgbBitCount = reader.ReadInt32();
            redBitMask = reader.ReadInt32();
            greenBitMask = reader.ReadInt32();
            blueBitMask = reader.ReadInt32();
            alphaBitMask = reader.ReadInt32();
        }

        #endregion        
    }

    /// <summary>
    /// Капсы
    /// </summary>
    class Caps
    {
        #region Атрибуты

        // DDS files should always include DDSCAPS_TEXTURE. If the file contains mipmaps, DDSCAPS_MIPMAP should be set. For any DDS file with more than one main surface, such as a mipmaps, cubic environment map, or volume texture, DDSCAPS_COMPLEX should also be set.
        private int caps1;
        // For cubic environment maps, DDSCAPS2_CUBEMAP should be included as well as one or more faces of the map (DDSCAPS2_CUBEMAP_POSITIVEX, DDSCAPS2_CUBEMAP_NEGATIVEX, DDSCAPS2_CUBEMAP_POSITIVEY, DDSCAPS2_CUBEMAP_NEGATIVEY, DDSCAPS2_CUBEMAP_POSITIVEZ, DDSCAPS2_CUBEMAP_NEGATIVEZ). For volume textures, DDSCAPS2_VOLUME should be included.
        private int caps2;

        #endregion

        #region Свойства

        /// <summary>
        /// DDS files should always include DDSCAPS_TEXTURE. If the file contains mipmaps, DDSCAPS_MIPMAP should be set. For any DDS file with more than one main surface, such as a mipmaps, cubic environment map, or volume texture, DDSCAPS_COMPLEX should also be set.
        /// </summary>
        public int Caps1
        {
            get { return caps1; }
        }

        /// <summary>
        /// For cubic environment maps, DDSCAPS2_CUBEMAP should be included as well as one or more faces of the map (DDSCAPS2_CUBEMAP_POSITIVEX, DDSCAPS2_CUBEMAP_NEGATIVEX, DDSCAPS2_CUBEMAP_POSITIVEY, DDSCAPS2_CUBEMAP_NEGATIVEY, DDSCAPS2_CUBEMAP_POSITIVEZ, DDSCAPS2_CUBEMAP_NEGATIVEZ). For volume textures, DDSCAPS2_VOLUME should be included.
        /// </summary>
        public int Caps2
        {
            get { return caps2; }
        }

        #endregion

        #region Инициализация
        
        /// <summary>
        /// Конструктор
        /// </summary>
        /// <param name="reader">Ридер данных</param>
        public Caps(BinaryReader reader)
        {
            caps1 = reader.ReadInt32();
            caps2 = reader.ReadInt32();
            int[] reserved = new int[] { reader.ReadInt32(), reader.ReadInt32() };
        }

        #endregion
    }

    /// <summary>
    /// Заголовок DDS файла
    /// </summary>
    class Header
    {
        #region Константы

        public static int DDSD_CAPS = 0x00000001;
        public static int DDSD_HEIGHT = 0x00000002;
        public static int DDSD_WIDTH = 0x00000004;
        public static int DDSD_PITCH = 0x00000008;
        public static int DDSD_PIXELFORMAT = 0x00001000;
        public static int DDSD_MIPMAPCOUNT = 0x00020000;
        public static int DDSD_LINEARSIZE = 0x00080000;
        public static int DDSD_DEPTH = 0x00800000;

        public static int DDPF_ALPHAPIXELS = 0x00000001; // Alpha channel is present
        public static int DDPF_ALPHA = 0x00000002; // Only contains alpha information
        public static int DDPF_FOURCC = 0x00000004; // FourCC code is valid
        public static int DDPF_PALETTEINDEXED4 = 0x00000008; // Surface is 4-bit color indexed
        public static int DDPF_PALETTEINDEXEDTO8 = 0x00000010; // Surface is indexed into a palette which stores indices
                                                                // into the destination surface's 8-bit palette
        public static int DDPF_PALETTEINDEXED8 = 0x00000020; // Surface is 8-bit color indexed
        public static int DDPF_RGB = 0x00000040; // RGB data is present
        public static int DDPF_COMPRESSED = 0x00000080; // Surface will accept pixel data in the format specified
                                                        // and compress it during the write
        public static int DDPF_RGBTOYUV = 0x00000100; // Surface will accept RGB data and translate it during
                                                      // the write to YUV data. The format of the data to be written
                                                      // will be contained in the pixel format structure. The DDPF_RGB
                                                      // flag will be set.
        public static int DDPF_YUV = 0x00000200; // Pixel format is YUV - YUV data in pixel format struct is valid
        public static int DDPF_ZBUFFER = 0x00000400; // Pixel format is a z buffer only surface
        public static int DDPF_PALETTEINDEXED1 = 0x00000800; // Surface is 1-bit color indexed
        public static int DDPF_PALETTEINDEXED2 = 0x00001000; // Surface is 2-bit color indexed
        public static int DDPF_ZPIXELS = 0x00002000; // Surface contains Z information in the pixels
        public static int DDPF_LUMINANCE = 0x00020000;

        public static int DDSCAPS_COMPLEX = 0x00000008;
        public static int DDSCAPS_TEXTURE = 0x00001000;
        public static int DDSCAPS_MIPMAP = 0x00400000;

        public static int DDSCAPS2_CUBEMAP = 0x00000200;
        public static int DDSCAPS2_CUBEMAP_POSITIVEX = 0x00000400;
        public static int DDSCAPS2_CUBEMAP_NEGATIVEX = 0x00000800;
        public static int DDSCAPS2_CUBEMAP_POSITIVEY = 0x00001000;
        public static int DDSCAPS2_CUBEMAP_NEGATIVEY = 0x00002000;
        public static int DDSCAPS2_CUBEMAP_POSITIVEZ = 0x00004000;
        public static int DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x00008000;
        public static int DDSCAPS2_VOLUME = 0x00200000;


        #endregion

        #region Атрибуты

        // Flags to indicate valid fields. Always include DDSD_CAPS, DDSD_PIXELFORMAT, DDSD_WIDTH, DDSD_HEIGHT.
        private int flags;
        // Height of the main image in pixels
        private int height;
        // Width of the main image in pixels
        private int width;
        // For uncompressed formats, this is the number of bytes per scan line (DWORD> aligned) for the main image. dwFlags should include DDSD_PITCH in this case. For compressed formats, this is the total number of bytes for the main image. dwFlags should be include DDSD_LINEARSIZE in this case.
        private int pitchOrLinearSize;
        // For volume textures, this is the depth of the volume. dwFlags should include DDSD_DEPTH in this case.
        private int depth;
        // For items with mipmap levels, this is the total number of levels in the mipmap chain of the main image. dwFlags should include DDSD_MIPMAPCOUNT in this case.        
        private int mipMapCount;
        // 32-byte value that specifies the pixel format structure.
        private PixelFormat pixelFormat;
        // 16-byte value that specifies the capabilities structure.
        private Caps caps;

        #endregion

        #region Свойства

        /// <summary>
        /// Flags to indicate valid fields. Always include DDSD_CAPS, DDSD_PIXELFORMAT, DDSD_WIDTH, DDSD_HEIGHT.
        /// </summary>
        public int Flags
        {
            get { return flags; }
        }

        /// <summary>
        /// Height of the main image in pixels
        /// </summary>
        public int Height
        {
            get { return height; }
        }

        /// <summary>
        /// Width of the main image in pixels
        /// </summary>
        public int Width
        {
            get { return width; }
        }

        /// <summary>
        /// For uncompressed formats, this is the number of bytes per scan line (DWORD> aligned) for the main image. dwFlags should include DDSD_PITCH in this case. For compressed formats, this is the total number of bytes for the main image. dwFlags should be include DDSD_LINEARSIZE in this case.
        /// </summary>
        public int PitchOrLinearSize
        {
            get { return pitchOrLinearSize; }
        }

        /// <summary>
        /// For volume textures, this is the depth of the volume. dwFlags should include DDSD_DEPTH in this case.
        /// </summary>
        public int Depth
        {
            get { return depth; }
        }

        /// <summary>
        /// For items with mipmap levels, this is the total number of levels in the mipmap chain of the main image. dwFlags should include DDSD_MIPMAPCOUNT in this case.        
        /// </summary>
        public int MipMapCount
        {
            get { return mipMapCount; }
        }

        /// <summary>
        /// 32-byte value that specifies the pixel format structure.
        /// </summary>
        public PixelFormat PixelFormat
        {
            get { return pixelFormat; }
        }

        /// <summary>
        /// 16-byte value that specifies the capabilities structure.
        /// </summary>
        public Caps Caps
        {
            get { return caps; }
        }

        #endregion

        #region Конструктор

        public Header(byte[] data)
        {
            if(data.Length!=124)throw new ApplicationException("Некорректный размер заголовка.");
            BinaryReader reader = new BinaryReader(new MemoryStream(data,false));
            int size = reader.ReadInt32();
            if(size!=124)throw new ApplicationException("Некорректный размер заголовка.");
            flags = reader.ReadInt32();
            height = reader.ReadInt32();
            width = reader.ReadInt32();
            pitchOrLinearSize = reader.ReadInt32();
            depth = reader.ReadInt32();
            mipMapCount = reader.ReadInt32();
            int[] reserved1 = new int[11];
            for (int i = 0; i < 11; i++) reserved1[i]= reader.ReadInt32();
            pixelFormat = new PixelFormat(reader);
            caps = new Caps(reader);
            int reserved2 = reader.ReadInt32();
            reader.Close();
        }

        #endregion

        #region Внутренее


        #endregion
    }
}
